package com.manage.gateway.auth;

import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.manage.common.core.constants.Constant;
import com.manage.common.core.dto.token.User;
import com.manage.common.core.enums.GrantType;
import com.manage.common.core.enums.ScopeType;
import com.manage.gateway.config.ApplicationProperties;
import com.nimbusds.jose.JWSObject;
import lombok.AllArgsConstructor;
import org.springframework.http.HttpMethod;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.security.authorization.AuthorizationDecision;
import org.springframework.security.authorization.ReactiveAuthorizationManager;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.server.authorization.AuthorizationContext;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.PathMatcher;
import org.springframework.util.StringUtils;
import reactor.core.publisher.Mono;

import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URLEncoder;
import java.text.ParseException;
import java.util.List;


/**
 * 鉴权管理器
 * @author zq
 */
@Component
@AllArgsConstructor
public class TokenAuthManager implements ReactiveAuthorizationManager<AuthorizationContext> {
    private final ApplicationProperties properties;

    @Override
    public Mono<AuthorizationDecision> check(Mono<Authentication> mono, AuthorizationContext authorizationContext) {
        ServerHttpRequest request = authorizationContext.getExchange().getRequest();
        URI uri = request.getURI();
        HttpMethod method = request.getMethod();

        // 跳过不需要验证的路径
        PathMatcher pathMatcher = new AntPathMatcher();
        String[] tokenFilterExclude = properties.getExclude().getTokenFilterExclude();
        for (String ignoreUrl : tokenFilterExclude) {
            if (pathMatcher.match(ignoreUrl, uri.getPath())) {
                return Mono.just(new AuthorizationDecision(true));
            }
        }

        //对应跨域的预检请求直接放行
        if(method== HttpMethod.OPTIONS){
            return Mono.just(new AuthorizationDecision(true));
        }
        //不同用户体系登录不允许互相访问
        try {
            String token = request.getHeaders().getFirst(Constant.JWT_TOKEN_HEADER);
            if(StrUtil.isEmpty(token)){
                return Mono.just(new AuthorizationDecision(false));
            }
            String realToken = token.replace(Constant.JWT_TOKEN_PREFIX, "");
            JWSObject jwsObject = JWSObject.parse(realToken);
            String userStr = jwsObject.getPayload().toString();
            User userDto = JSONUtil.toBean(userStr, User.class);
            if (Constant.SYSTEM_CLIENT_ID.equals(userDto.getClientId())) {
                if(!pathMatcher.match(Constant.SYSTEM_URL_PATTERN, uri.getPath())&&
                        !pathMatcher.match(Constant.OAUTH_URL_PATTERN, uri.getPath())) {
                    return Mono.just(new AuthorizationDecision(false));
                }
            }

            // 客户端模式
            if(GrantType.CLIENT_CREDENTIALS.getCode().equals(userDto.getGrantType())){
                List<String> scope = userDto.getScope();
                // 1.如果权限范围包含all则标示可以处理全部请求直接放行
                if(scope.contains(ScopeType.ALL.getCode())){
                    return Mono.just(new AuthorizationDecision(true));
                }
                // 2.如果权限范围不包含read,请求方式是get请求则拒绝请求
                if(method == HttpMethod.GET){
                    if(!scope.contains(ScopeType.READ.getCode())){
                        return Mono.just(new AuthorizationDecision(false));
                    }else {
                        return Mono.just(new AuthorizationDecision(true));
                    }
                }
                // 3.如果权限范围是write,请求方式是post,put,delete则放行
                if(method == HttpMethod.PUT|| method == HttpMethod.POST|| method == HttpMethod.DELETE){
                    if(scope.contains(ScopeType.WRITE.getCode())){
                        return Mono.just(new AuthorizationDecision(true));
                    }else {
                        return Mono.just(new AuthorizationDecision(false));
                    }
                }
            }
            if(!StringUtils.isEmpty(userDto.getUserId())){
                // 设置userId到请求头
                request.mutate().header(Constant.CURRENT_USER, URLEncoder.encode(userStr, "UTF-8"));
            }
        } catch (ParseException | UnsupportedEncodingException e) {
            e.printStackTrace();
            return Mono.just(new AuthorizationDecision(false));
        }
        return Mono.just(new AuthorizationDecision(true));
    }

}
